//
// Created by Administrator on 2022/1/17.
//

#include "Sphere.h"

Sphere::Sphere()
{
    Init(48);
}

Sphere::Sphere(int precision)
{
    Init(precision);
}

// 这里的处理是把球的两个极点也拆分成了precision个顶点，只不过都是相同的
// 这样就避免了索引顶点的时候特殊处理
// precision指的是垂直和水平方向分了多少份
// 举个例子，如果precision是4，那么水平方向上将切3刀，分为了4份，垂直方向上
// 也是4份，4个顶点。
void Sphere::Init(int precision)
{
    // 顶点总数是层数 * 每层的顶点数
    // 这里由于是将极点也看做一层，因此两个极点+中间precision-1层，共有precision+1层
    // 另外，考虑到uv，由于每一层的第一个顶点u坐标是0，最后一个顶点的u坐标是
    // precision - 1 / precision，插值就将发生在(precision-1/precision, 0)
    // 之间，而不是期望的(precision-1/precision, 1)，因此需要再增加一个顶点，它
    // 和第一个顶点重合，u坐标为1，保证插值正确，所以这样，每层就有precision+1个顶点
    // 因此总的定点数就是(precision + 1) * (precision + 1)
    numVertices = (precision + 1) * (precision + 1);
    // 索引总数是顶点数 * 每个顶点生成的索引数（一个顶点可以和右、上、右上三个方向的邻接顶点构成2个三角形
    // 因此总计能生成6个索引）
    // 但注意，索引只会遍历部分顶点，而非所有，这些顶点需要在右、上和右上三个方向上
    // 有相邻顶点，并且能构成三角形，因此如果从下极点开始遍历，那么上极点的这一层中的顶点
    // 就因为没有上和右上方向的相邻顶点而不能作为索引遍历的顶点，因此层数就是precision
    // 同理，每一层上，最后的一个顶点是没有右、上、右上三个方向的邻接顶点的，它和每层的
    // 第一个顶点无法构成三角形，这是因为每层的最后一个顶点实际上和第一个顶点是重合的，
    // 可见上面的推导（为了uv坐标考虑而增加的一个顶点），因此每一层的顶点数为precision
    numIndices = precision * precision * 6;

    // 计算顶点
    // 利用极坐标计算(y朝上，x朝右，z朝前，右手坐标系，theta从x轴开始，逆时针旋转，
    // 因此，z值计算的时候，需要取-
    float deltaTheta = PI / float(precision);
    float deltaPhi = PI_2 / float(precision);
    for(int i = 0; i <= precision; ++i)
    {
        for(int j = 0; j <= precision; ++j)
        {
            float theta = PI - float(i) * deltaTheta;
            float phi = float(j) * deltaPhi;
            float y = cos(theta);
            float x = (i == 0 || i == precision) ? 0.0f : sin(theta) * cos(phi);
            float z = (i == 0 || i == precision) ? 0.0f : -sin(theta) * sin(phi);
            vertices.emplace_back(x, y, z);
            uvs.emplace_back(glm::vec2(float(j) / float(precision), float(i) / float(precision)));
            normals.emplace_back(glm::normalize(glm::vec3(x, y, z))); // 保险起见，normalize一次
        }
    }

    // 计算索引
    // i * n + j  // base 0
    // i * n + j + 1 // 右 1
    // (i + 1) * n + j + 1 // 右上 2
    // (i + i) * n + j // 上 3
    // n是每层的顶点数，precision + 1（注意之前的推导，为什么会是+1）
    // 这四个顶点构成2个三角形，6个索引，按逆时针，顺序是
    // 0-1-3 1-2-3
    int n = precision + 1;
    for(int i = 0; i < precision; ++i)
    {
        for(int j = 0; j < precision; ++j)
        {
            indices.emplace_back(i * n + j);
            indices.emplace_back(i * n + j + 1);
            indices.emplace_back((i + 1) * n + j);
            indices.emplace_back(i * n + j + 1);
            indices.emplace_back((i + 1) * n + j + 1);
            indices.emplace_back((i + 1) * n + j);
        }
    }
}